/*
 * Copyright (c) 2021
 * User:LENOVO
 * File:省份数量.java
 * Date:2021/02/18 14:18:18
 */

package org.bytedance.数组与排序;


import java.util.LinkedList;
import java.util.Queue;

/**
 * 有 n 个城市，其中一些彼此相连，另一些没有相连。如果城市 a 与城市 b 直接相连，且城市 b 与城市 c 直接相连，那么城市 a 与城市 c 间接相连。
 * <p>
 * 省份 是一组直接或间接相连的城市，组内不含其他没有相连的城市。
 * <p>
 * 给你一个 n x n 的矩阵 isConnected ，其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连，而 isConnected[i][j] = 0 表示二者不直接相连。
 * <p>
 * 返回矩阵中 省份 的数量。
 */
public class 省份数量 {

    public static void main(String[] args) {
        省份数量 instance = new 省份数量();

        int[][] reference = new int[][]{
                new int[]{1, 1, 0},
                new int[]{1, 1, 0},
                new int[]{0, 0, 1}
        };
        int[][] reference2 = new int[][]{
                new int[]{1, 0, 0},
                new int[]{0, 1, 0},
                new int[]{0, 0, 1}
        };
        instance.printMatrix(reference);

        System.out.println("----------------");

        instance.printMatrix(reference2);
        int result = instance.findCircleNum(reference);
        System.out.println(result);
        result = instance.findCircleNum(reference2);
        System.out.println(result);

        result = instance.findCircleNum广度(reference);
        System.out.println(result);


        result = instance.findCircleNum广度(reference2);
        System.out.println(result);


        result = instance.findCircleNum并查集(reference);
        System.out.println(result);

        result = instance.findCircleNum并查集(reference2);
        System.out.println(result);


    }

    /**
     * 深度优先遍历
     * 深度优先搜索的思路是很直观的。遍历所有城市，对于每个城市，如果该城市尚未被访问过，则从该城市开始深度优先搜索，
     * 通过矩阵 \textit{isConnected}isConnected 得到与该城市直接相连的城市有哪些，
     * 这些城市和该城市属于同一个连通分量，然后对这些城市继续深度优先搜索，直到同一个连通分量的所有城市都被访问到，即可得到一个省份。
     * 遍历完全部城市以后，即可得到连通分量的总数，即省份的总数。
     *
     * @param isConnected
     * @return
     */
    public int findCircleNum(int[][] isConnected) {
        int provinces = isConnected.length;
        boolean[] visited = new boolean[provinces];
        int circles = 0;
        for (int i = 0; i < provinces; i++) {
            if (!visited[i]) {
                dfs(isConnected, visited, provinces, i);
                circles++;
            }
        }
        return circles;
    }

    private void dfs(int[][] isConnected, boolean[] visited, int provinces, int i) {
        for (int j = 0; j < provinces; j++) {
            if (isConnected[i][j] == 1 && !visited[j]) {
                visited[j] = true;
                dfs(isConnected, visited, provinces, j);
            }
        }
    }

    /**
     * 广度优先遍历
     * 也可以通过广度优先搜索的方法得到省份的总数。对于每个城市，如果该城市尚未被访问过，则从该城市开始广度优先搜索，
     * 直到同一个连通分量中的所有城市都被访问到，即可得到一个省份。
     */
    public int findCircleNum广度(int[][] isConnected) {
        int provinces = isConnected.length;
        boolean[] visited = new boolean[provinces];
        int circles = 0;
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < provinces; i++) {
            if (!visited[i]) {
                queue.offer(i);
                while (!queue.isEmpty()) {
                    int j = queue.poll();
                    visited[j] = true;
                    for (int k = 0; k < provinces; k++) {
                        if (isConnected[j][k] == 1 && !visited[k]) {
                            queue.offer(k);
                        }
                    }
                }
                circles++;
            }
        }
        return circles;
    }


    private void printMatrix(int[][] isConnected) {
        for (int i = 0; i < isConnected.length; i++) {
            for (int j = 0; j < isConnected[i].length; j++) {
                System.out.print(isConnected[i][j] + " ");
            }
            System.out.println();
        }
    }

    /**
     * 并查集
     */
    public int findCircleNum并查集(int[][] isConnected) {
        int provinces = isConnected.length;
        int[] parent = new int[provinces];
        for (int i = 0; i < provinces ; i++) {
            parent[i] = i;
        }
        for (int i = 0; i <  provinces; i++) {
            for (int j = i + 1; j < provinces ; j++) {
                if (isConnected[i][j] == 1){
                    union(parent, i, j);
                }
            }
        }
        int circles = 0;
        for (int i = 0; i < provinces; i++) {
            if (parent[i] == i){
                circles++;
            }
        }
        return circles;

    }

    private void union(int[] parent, int index1, int index2) {
        parent[find(parent, index1)] = find(parent, index2);
    }

    private int find(int[] parent, int index) {
        if (parent[index] != index) {
            parent[index] = find(parent, parent[index]);
        }
        return parent[index];
    }


}
